﻿/* ----------------------------------------------------------------------------
 * Copyright (c) Rob Ashton 2009
 *
 * This source code is subject to terms and conditions of the Microsoft Public 
 * License (Ms-PL).
 * 
 * By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 * 
 * To request a copy of the Microsoft Public License, send a request by email
 * to license@mvcex.com
 *
 * This notice must not be removed from this software.
 * ---------------------------------------------------------------------------- */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using System.Web.Mvc;
using System.Reflection;
using MvcEx.Controllers;

namespace MvcEx.Tests
{
    [TestFixture]
    public class MvcExControllerBuilderTests
    {
        [Test]
        public void create_builder_from_configuration()
        {
            // Create the configuration
            MvcExControllerBuilderConfiguration configuration = new MvcExControllerBuilderConfiguration();

            // Populate it
            configuration.BuilderIdentifier = "Test";
            configuration.RegisterAssembly(typeof(MvcExControllerBuilderTests).Assembly);
                       
            // Create the builder
            MvcExControllerBuilder builder = new MvcExControllerBuilder(configuration);
        }

        [Test]
        public void create_builder_from_configuration_with_duplicate_assemblies()
        {
            // Create the configuration
            MvcExControllerBuilderConfiguration configuration = new MvcExControllerBuilderConfiguration();

            // Populate it, twice with the same assembly though
            configuration.BuilderIdentifier = "Test";
            configuration.RegisterAssembly(typeof(MvcExControllerBuilderTests).Assembly);
            configuration.RegisterAssembly(typeof(MvcExControllerBuilderTests).Assembly);

            // Create the builder
            MvcExControllerBuilder builder = new MvcExControllerBuilder(configuration);
        }
        
        [Test]
        public void create_empty_controller()
        {
            // Create the factory
            MvcExControllerBuilderConfiguration config = new MvcExControllerBuilderConfiguration();
            config.RegisterAssembly(typeof(EmptyContainer).Assembly);
            MvcExControllerBuilder factory = new MvcExControllerBuilder(config);

            // Request the empty controller type
            Type controllerType = factory.GetControllerType("Empty");

            // Validate it's the right type
            Assert.NotNull(controllerType);
            Assert.IsTrue(typeof(MvcExController).IsAssignableFrom(controllerType));
        }

        [Test]
        public void create_invalid_controller()
        {
            // Create the factory
            MvcExControllerBuilderConfiguration config = new MvcExControllerBuilderConfiguration();
            config.RegisterAssembly(typeof(EmptyContainer).Assembly);
            MvcExControllerBuilder factory = new MvcExControllerBuilder(config);

            // Request the invalid controller
            Type controllerType = factory.GetControllerType("Invalid");

            // Validate we got nothing back
            Assert.Null(controllerType);
        }

        [Test]
        public void create_controller_with_single_empty_index()
        {
            // Create the factory
            MvcExControllerBuilderConfiguration config = new MvcExControllerBuilderConfiguration();
            config.RegisterAssembly(typeof(EmptyContainer).Assembly);
            MvcExControllerBuilder factory = new MvcExControllerBuilder(config);

            // Request the SingleEmptyIndex controller
            Type controllerType = factory.GetControllerType("SingleEmptyIndex");

            // Validate we got something back
            Assert.NotNull(controllerType);
        }

        [Test]
        public void create_controller_with_action_args()
        {
            // Create the factory
            MvcExControllerBuilderConfiguration config = new MvcExControllerBuilderConfiguration();
            config.RegisterAssembly(typeof(EmptyContainer).Assembly);
            MvcExControllerBuilder factory = new MvcExControllerBuilder(config);

            // Request the HasArgs controller
            Type controllerType = factory.GetControllerType("HasArgs");

            // Validate we got something back
            Assert.NotNull(controllerType);
        }

        [Test]
        public void create_controller_with_single_empty_index_validate_calls()
        {
            // Create the factory
            MvcExControllerBuilderConfiguration config = new MvcExControllerBuilderConfiguration();
            config.RegisterAssembly(typeof(EmptyContainer).Assembly);
            MvcExControllerBuilder factory = new MvcExControllerBuilder(config);

            // Request the SingleEmptyIndex controller
            Type controllerType = factory.GetControllerType("SingleEmptyIndex");

            // Validate we got something back
            Assert.NotNull(controllerType);

            // Create the controller
            Object controller = Activator.CreateInstance(controllerType);

            // Call the action method
            ViewResult result = TestHelper.CallMethod(controller, "Index") as ViewResult;

            // And validate we get a view result
            Assert.NotNull(result);
        }

        [Test]
        public void create_controller_with_action_args_validate_calls()
        {
            // Create the factory
            MvcExControllerBuilderConfiguration config = new MvcExControllerBuilderConfiguration();
            config.RegisterAssembly(typeof(EmptyContainer).Assembly);
            MvcExControllerBuilder factory = new MvcExControllerBuilder(config);

            // Request the HasArgs type
            Type controllerType = factory.GetControllerType("HasArgs");

            // Validate we got something back
            Assert.NotNull(controllerType);

             // Create the controller
            Object controller = Activator.CreateInstance(controllerType);                  

            // Call the method with some args
            ViewResult result = TestHelper.CallMethod(controller, "Index", "SomeArg1", "SomeArg2") as ViewResult;

            // And validate we get a view result
            Assert.NotNull(result);

            // With the right details
            Assert.AreEqual("SomeArg1SomeArg2", result.ViewName);
        }

        [Test]
        public void create_controller_with_custom_attribute()
        {
            // Create the factory
            MvcExControllerBuilderConfiguration config = new MvcExControllerBuilderConfiguration();
            config.RegisterAssembly(typeof(EmptyContainer).Assembly);
            MvcExControllerBuilder factory = new MvcExControllerBuilder(config);
            
            // Request the HasAttribute controller
            Type controllerType = factory.GetControllerType("HasAttribute");
            
            // Validate we got something back
            Assert.NotNull(controllerType);
            
            // Get the method
            MethodInfo info = controllerType.GetMethod("Index", BindingFlags.Instance | BindingFlags.Public);
            Assert.AreEqual(1, info.GetCustomAttributes(typeof(AuthorizeAttribute), false).Length);
        }

        [Test]
        public void create_controller_with_duplicate_methods()
        {
            // Create the factory
            MvcExControllerBuilderConfiguration config = new MvcExControllerBuilderConfiguration();
            config.RegisterAssembly(typeof(EmptyContainer).Assembly);
            MvcExControllerBuilder factory = new MvcExControllerBuilder(config);

            // Request the duplicate controller
            Type controllerType = factory.GetControllerType("DuplicateContainer");

            // Validate we got something back
            Assert.NotNull(controllerType);

            // Get the methods (there should only be one, we should be ignoring one of them)
            MethodInfo[] info = controllerType.GetMethods().Where(x => x.Name == "Index").ToArray();
            Assert.AreEqual(1, info.Length);
        }

        [Test]
        public void create_controller_with_parameterised_stringcontainer_and_verify_calling()
        {
            // Create the factory
            MvcExControllerBuilderConfiguration config = new MvcExControllerBuilderConfiguration();
            config.RegisterAssembly(typeof(ParameterisedStringContainer1).Assembly);
            MvcExControllerBuilder factory = new MvcExControllerBuilder(config);

            // Request the duplicate controller
            Type controllerType = factory.GetControllerType("ParameterisedStringContainer1");
                        
            // Validate we got something back
            Assert.NotNull(controllerType);

            Object controller = Activator.CreateInstance(controllerType);
            MethodInfo indexMethod = controllerType.GetMethod("Index");
            indexMethod.Invoke(controller, null);
        }

        [Test]
        public void create_controller_with_parameterised_complexcontainer_and_verify_calling()
        {
            // Create the factory
            MvcExControllerBuilderConfiguration config = new MvcExControllerBuilderConfiguration();
            config.RegisterAssembly(typeof(ParameterisedComplexContainer1).Assembly);
            MvcExControllerBuilder factory = new MvcExControllerBuilder(config);

            // Request the duplicate controller
            Type controllerType = factory.GetControllerType("ParameterisedComplexContainer1");
                        
            // Validate we got something back
            Assert.NotNull(controllerType);

            Object controller = Activator.CreateInstance(controllerType);
            MethodInfo indexMethod = controllerType.GetMethod("Index");
            indexMethod.Invoke(controller, null);
        }

        [Test]
        public void create_controller_with_parameterised_primitivecontainer_and_verify_calling()
        {
            // Create the factory
            MvcExControllerBuilderConfiguration config = new MvcExControllerBuilderConfiguration();
            config.RegisterAssembly(typeof(ParameterisedComplexContainer1).Assembly);
            MvcExControllerBuilder factory = new MvcExControllerBuilder(config);
            
            // Request the duplicate controller
            Type controllerType = factory.GetControllerType("ParameterisedPrimitiveContainer1");

            // Validate we got something back
            Assert.NotNull(controllerType);

            Object controller = Activator.CreateInstance(controllerType);
            MethodInfo indexMethod = controllerType.GetMethod("Index");
            indexMethod.Invoke(controller, null);
        }

        [Test]
        public void create_controller_with_parameterised_container_and_verify_injection()
        {
            // Create the factory
            MvcExControllerBuilderConfiguration config = new MvcExControllerBuilderConfiguration();
            config.RegisterAssembly(typeof(ParameterisedComplexContainer1).Assembly);
            MvcExControllerBuilder factory = new MvcExControllerBuilder(config);

            // Request the duplicate controller
            Type controllerType = factory.GetControllerType("ParameterisedStringContainer1");

            // Validate we got something back
            Assert.NotNull(controllerType);

            Object controller = Activator.CreateInstance(controllerType, new TestInjector());
            MethodInfo indexMethod = controllerType.GetMethod("Index");
            Object returnValue = indexMethod.Invoke(controller, null);
        }

        public class TestInjector : IIocContainer
        {
            #region IIocContainer Members

            public object GetInstance(Type t)
            {
                return "Hello";
            }

            #endregion
        }

        public class SomeType
        { }

        [MvcExActionContainer("ParameterisedPrimitiveContainer1")]
        public class ParameterisedPrimitiveContainer1 : MvcExActionContainer
        {
            private int mArg;
            public ParameterisedPrimitiveContainer1(int arg)
            {
                mArg = arg;
            }

            public ActionResult Index()
            {
                return View();
            }
        }

        
        [MvcExActionContainer("ParameterisedComplexContainer1")]
        public class ParameterisedComplexContainer1 : MvcExActionContainer
        {
            private SomeType mArg;
            public ParameterisedComplexContainer1(SomeType arg)
            {
                mArg = arg;
            }

            public ActionResult Index()
            {
                return View();
            }
        }

        [MvcExActionContainer("ParameterisedStringContainer1")]
        public class ParameterisedStringContainer1 : MvcExActionContainer
        {
            private String mArg;
            public ParameterisedStringContainer1(String arg)
            {
                mArg = arg;
            }

            public ActionResult Index()
            {
                return View();
            }
        }

        [MvcExActionContainer("DuplicateContainer")]
        public class DuplicateContainer1 : MvcExActionContainer
        {
            public ActionResult Index()
            {
                return View();
            }
        }

        [MvcExActionContainer("DuplicateContainer")]
        public class DuplicateContainer2 : MvcExActionContainer
        {
            public ActionResult Index()
            {
                return View();
            }
        }

        [MvcExActionContainer("Empty")]
        public class EmptyContainer : MvcExActionContainer
        {

        }

        [MvcExActionContainer("SingleEmptyIndex")]
        public class SingleEmptyIndexContainer : MvcExActionContainer
        {
            public ActionResult Index()
            {
                return View();
            }
        }

        [MvcExActionContainer("HasArgs")]
        public class HasArgsContainer : MvcExActionContainer
        {
            public ActionResult Index(String arg1, String arg2)
            {
                // Return the data so we know we routed it properly
                return View(String.Format("{0}{1}", arg1, arg2));
            }
        }

        [MvcExActionContainer("HasAttribute")]
        public class HasAttributesContainer : MvcExActionContainer
        {
            [Authorize()]
            public ActionResult Index()
            {
                return View();
            }
        }
    }
}
